const sjcl = require('sjcl.js')


function add(x, y) {

  return ((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) ^ (x & 0x80000000) ^ (y & 0x80000000);

}



function SHA1hex(num) {

  var sHEXChars = "0123456789abcdef";

  var str = "";

  for (var j = 7; j >= 0; j--)

    str += sHEXChars.charAt((num >> (j * 4)) & 0x0F);

  return str;

}



function AlignSHA1(sIn) {

  var nblk = ((sIn.length + 8) >> 6) + 1,

    blks = new Array(nblk * 16);

  for (var i = 0; i < nblk * 16; i++) blks[i] = 0;

  for (i = 0; i < sIn.length; i++)

    blks[i >> 2] |= sIn.charCodeAt(i) << (24 - (i & 3) * 8);

  blks[i >> 2] |= 0x80 << (24 - (i & 3) * 8);

  blks[nblk * 16 - 1] = sIn.length * 8;

  return blks;

}



function rol(num, cnt) {

  return (num << cnt) | (num >>> (32 - cnt));

}



function ft(t, b, c, d) {

  if (t < 20) return (b & c) | ((~b) & d);

  if (t < 40) return b ^ c ^ d;

  if (t < 60) return (b & c) | (b & d) | (c & d);

  return b ^ c ^ d;

}



function kt(t) {

  return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :

    (t < 60) ? -1894007588 : -899497514;

}



function de1(sIn) {

  var x = AlignSHA1(sIn);

  var w = new Array(80);

  var a = 1732584193;

  var b = -271733879;

  var c = -1732584194;

  var d = 271733878;

  var e = -1009589776;

  for (var i = 0; i < x.length; i += 16) {

    var olda = a;

    var oldb = b;

    var oldc = c;

    var oldd = d;

    var olde = e;

    for (var j = 0; j < 80; j++) {

      if (j < 16) w[j] = x[i + j];

      else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);

      var t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));

      e = d;

      d = c;

      c = rol(b, 30);

      b = a;

      a = t;

    }

    a = add(a, olda);

    b = add(b, oldb);

    c = add(c, oldc);

    d = add(d, oldd);

    e = add(e, olde);

  }

  var SHA1Value = SHA1hex(a) + SHA1hex(b) + SHA1hex(c) + SHA1hex(d) + SHA1hex(e);

  return SHA1Value.toUpperCase();

}



function de2(sIn) {

  return de1(sIn).toLowerCase();

}

function getEncodeString (srcString) {
  //var srcString = 'abc';  
  var BASE32CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  var i = 0;
  var index = 0;
  var digit = 0;
  var currByte;
  var nextByte;
  var retrunString = '';

  for (var i = 0; i < srcString.length;) {
    //var          index    = 0;  
    currByte = (srcString.charCodeAt(i) >= 0) ? srcString.charCodeAt(i)
      : (srcString.charCodeAt(i) + 256);

    if (index > 3) {
      if ((i + 1) < srcString.length) {
        nextByte = (srcString.charCodeAt(i + 1) >= 0)
          ? srcString.charCodeAt(i + 1)
          : (srcString.charCodeAt(i + 1) + 256);
      } else {
        nextByte = 0;
      }

      digit = currByte & (0xFF >> index);
      index = (index + 5) % 8;
      digit <<= index;
      digit |= (nextByte >> (8 - index));
      i++;
    } else {
      digit = (currByte >> (8 - (index + 5))) & 0x1F;
      index = (index + 5) % 8;

      if (index == 0) {
        i++;
      }
    }

    retrunString = retrunString + BASE32CHAR.charAt(digit);
  }
  return retrunString;
}  
var getDecodeString = function (encodeString) {
  var BASE32LOOOKUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  var i;
  var index;
  var lookup;
  var offset;
  var digit;
  var encodeString = encodeString.toUpperCase();
  var stringLen = parseInt((encodeString.length * 5) / 8);
  var bytes = new Array(stringLen);
  for (var a = 0; a < stringLen; a++) {
    bytes[a] = 0;
  }
  for (i = 0, index = 0, offset = 0; i < encodeString.length; i++) {
    var charCode0 = '0'.charCodeAt(0);
    lookup = encodeString.charCodeAt(i) - charCode0;
    if ((lookup < 0) || (lookup >= BASE32LOOOKUP.length)) {
      continue;
    }
    digit = BASE32LOOOKUP[lookup];
    if (digit == 0xFF) {
      continue;
    }
    if (index <= 3) {
      index = (index + 5) % 8;
      if (index == 0) {
        bytes[offset] = bytes[offset] | digit;
        offset++;
        if (offset >= bytes.length) {
          break;
        }
      } else {
        bytes[offset] = bytes[offset] | (digit << (8 - index));
      }
    } else {
      index = (index + 5) % 8;
      bytes[offset] = bytes[offset] | (digit >>> index);
      offset++;
      if (offset >= bytes.length) {
        break;
      }
      bytes[offset] = bytes[offset] | (digit << (8 - index));
      if (bytes[offset] >= 256) {
        //var lp = parseInt(bytes[offset]/256);  
        bytes[offset] %= 256;
      }
    }
  }
  //return bytes.join(',');  
  var realkeyString = '';
  var decodeString = '';
  for (var a = 0; a < bytes.length; a++) {
    var realkey = String.fromCharCode(bytes[a]);
    realkeyString += realkey;
    //decodeString += bytes[a];  
  }
  return realkeyString;
}   



function GenerateHOTP(K, C) {
  var key = sjcl.codec.base32.toBits(K);
  // Count is 64 bits long.  Note that JavaScript bitwise operations make
  // the MSB effectively 0 in this case.
  var count = [((C & 0xffffffff00000000) >> 32), C & 0xffffffff];
  var otplength = 6;

  var hmacsha1 = new sjcl.misc.hmac(key, sjcl.hash.sha1);
  var code = hmacsha1.encrypt(count);

  var offset = sjcl.bitArray.extract(code, 152, 8) & 0x0f;
  var startBits = offset * 8;
  var endBits = startBits + 4 * 8;
  var slice = sjcl.bitArray.bitSlice(code, startBits, endBits);
  var dbc1 = slice[0];
  var dbc2 = dbc1 & 0x7fffffff;
  var otp = dbc2 % Math.pow(10, otplength);
  var result = otp.toString();
  while (result.length < otplength) {
    result = '0' + result;
  }
  return result;
}

function hotp(secret, number){
  var key = getEncodeString(secret);
  
}
function totp(secret){
  var number;
  return hotp(secret, number = parseInt(parseInt(new Date.getTime()/1000)) / 43200)
}

function get_secret_key(){
  var totp_secret_key = '^423q4+f3mu2+_to3e812^6%^(cs+15)%@h!_29p)k1snlx)sz'
  return totp(getEncodeString(totp_secret_key))
}

function stringToByte(str) {
  var bytes = new Array();
  var len, c;
  len = str.length;
  for (var i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if (c >= 0x010000 && c <= 0x10FFFF) {
      bytes.push(((c >> 18) & 0x07) | 0xF0);
      bytes.push(((c >> 12) & 0x3F) | 0x80);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000800 && c <= 0x00FFFF) {
      bytes.push(((c >> 12) & 0x0F) | 0xE0);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000080 && c <= 0x0007FF) {
      bytes.push(((c >> 6) & 0x1F) | 0xC0);
      bytes.push((c & 0x3F) | 0x80);
    } else {
      bytes.push(c & 0xFF);
    }
  }
  return bytes;


}

module.exports.de1 = de1;
module.exports.de2 = de2;
module.exports.getEncodeString = getEncodeString;
module.exports.getDecodeString = getDecodeString;
module.exports.stringToByte = stringToByte;
